﻿#region All usings
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion


namespace senSimulator.DrawableComponents
{
    /// <summary>
    /// The class "Traffic"
    /// </summary>
    class Traffic : DrawableComponent
    {
        /// <summary>
        /// Variables for the start and end location (North to west)
        /// </summary>
        protected Vector2[] _wayPoints          = null;
        protected int _currentWayPoint          = 0;
        protected float _speed                  = 0;
        protected int _length                   = 0;
        protected bool _move                    = false;
        protected bool _finished                = false;
        protected string _from                  = "";
        protected string _to                    = "";
        protected bool _sensorTriggered         = false;
        protected Vector2 _stopForTrafficVector = Vector2.Zero;
        protected Vector2 _currentMoveVector    = Vector2.Zero;
        protected string _type;

        // Angle settings of traffic
        protected float _angleOffset                                = 0;
        protected Dictionary<string, float> _preRenderedDirections  = new Dictionary<string, float>();

        // Collision fix
        protected float _margin = 1.0f;


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="game">The main game class</param>
        /// <param name="textures">All textures</param>
        /// <param name="posX">Position X</param>
        /// <param name="posY">Position Y</param>
        /// <param name="centerX">Center X</param>
        /// <param name="centerY">Center Y</param>
        /// <param name="rotationInDegrees">Rotation in degrees</param>
        public Traffic(Game game, Texture2D textureName, Vector2 textureCenter, int posX, int posY, int rotationInDegrees, float speed, string from, string to, string type)
            : base(game, new Texture2D[] {textureName}, new Vector2[] {textureCenter}, posX, posY, rotationInDegrees)
        {
            _speed = speed;
            _length = textureName.Height;
            _from = from;
            _to = to;
            _angleOffset = (float)(Math.PI * 90 / 180);
            PreRenderMovingAngles();
            _type = type;
        }


        /// <summary>
        /// Precalculate every moving direction to gain performance
        /// </summary>
        private void PreRenderMovingAngles()
        {
            _preRenderedDirections.Add("00", (float) Math.Atan2(0, 0));
            _preRenderedDirections.Add("0" + _speed.ToString(), (float)Math.Atan2(0, _speed));
            _preRenderedDirections.Add("0-" + _speed.ToString(), (float)Math.Atan2(0, -_speed));
            _preRenderedDirections.Add(_speed.ToString() + "0", (float)Math.Atan2(_speed, 0));
            _preRenderedDirections.Add("-" + _speed.ToString() + "0", (float)Math.Atan2(-_speed, 0));
            _preRenderedDirections.Add(_speed.ToString() + _speed.ToString(), (float)Math.Atan2(_speed, _speed));
            _preRenderedDirections.Add("-" + _speed.ToString() + "-" + _speed.ToString(), (float)Math.Atan2(-_speed, -_speed));
            _preRenderedDirections.Add(_speed.ToString() + "-" + _speed.ToString(), (float)Math.Atan2(_speed, -_speed));
            _preRenderedDirections.Add("-" + _speed.ToString() + _speed.ToString(), (float)Math.Atan2(-_speed, _speed));
        }


        /// <summary>
        /// Get the length of a traffic type
        /// </summary>
        /// <returns></returns>
        public int GetLength()
        {
            return _length;
        }


        /// <summary>
        /// Set the list of way points (coördinates to walk/drive to)
        /// </summary>
        /// <param name="wayPoints">List of way points</param>
        public void SetWayPoints(Vector2[] wayPoints)
        {
            _wayPoints = wayPoints;
            _currentWayPoint = 0;
        }


        /// <summary>
        /// Get the current way point
        /// </summary>
        /// <returns>Integer/index of the way point</returns>
        public int GetCurrentWayPoint()
        {
            return _currentWayPoint;
        }


        /// <summary>
        /// Returns the direction from where the vehicle is comming.
        /// </summary>
        /// <returns>Direction from</returns>
        public string GetFrom()
        {
            return _from;
        }


        /// <summary>
        /// Returns the direction in which the vehicle is driving
        /// </summary>
        /// <returns>Direction to</returns>
        public string GetTo()
        {
            return _to;
        }


        /// <summary>
        /// Get the first waypoint
        /// </summary>
        /// <returns></returns>
        public Vector2 GetFirstWayPoint()
        {
            return _wayPoints[0];
        }


        /// <summary>
        /// Get the boolean sensorTriggered
        /// </summary>
        /// <returns>Sensor triggered?</returns>
        public bool SensorTriggered()
        {
            return _sensorTriggered;
        }


        /// <summary>
        /// Set the sensor triggered
        /// </summary>
        /// <param name="triggered"></param>
        public void SetSensorTriggered(bool triggered)
        {
            _sensorTriggered = triggered;
        }


        /// <summary>
        /// Gets the traffic type
        /// </summary>
        /// <returns>car/bicycle/bus/pedestrian</returns>
        public string GetTrafficType()
        {
            return _type;
        }

        /// <summary>
        /// Continue moving
        /// </summary>
        public void Move()
        {
            _move = true;
        }


        /// <summary>
        /// Continue moving to stopForCar Vector2
        /// </summary>
        /// <param name="stopForCar">stopForCar</param>
        public void Move(Vector2 stopForCar, int length)
        {
            switch (_from)
            {
                case "N":
                    stopForCar.Y -= length;
                    break;
                case "W":
                    stopForCar.X -= length;
                    break;
                case "S":
                    stopForCar.Y += length;
                    break;
                case "E":
                    stopForCar.X += length;
                    break;
            }

            _stopForTrafficVector = stopForCar;
            Move();
        }


        /// <summary>
        /// Stop moving
        /// </summary>
        public void Stop()
        {
            _move = false;
        }


        /// <summary>
        /// Gives a response if the traffic type has finished it's movement
        /// </summary>
        /// <returns>Finished</returns>
        public bool Finished()
        {
            return _finished;
        }


        /// <summary>
        /// Move the traffic based on the way points
        /// </summary>
        public virtual void MoveToWayPoint()
        {
            if(GetPosition() != _wayPoints[_currentWayPoint])
            {
                // Check which direction
                float xDistance = 0;
                float yDistance = 0;

                // Check if a temporary vector is set (for other traffic positions)
                if (_stopForTrafficVector != Vector2.Zero && GetPosition() != _stopForTrafficVector)
                {
                    xDistance = GetPosition().X - _stopForTrafficVector.X;
                    yDistance = GetPosition().Y - _stopForTrafficVector.Y;
                }
                else // Else drive towards the next position
                {
                    _stopForTrafficVector = Vector2.Zero;
                    xDistance = GetPosition().X - _wayPoints[_currentWayPoint].X;
                    yDistance = GetPosition().Y - _wayPoints[_currentWayPoint].Y;
                }


                float moveX = 0;
                float moveY = 0;
                if (xDistance > 0)
                    moveX = -_speed;
                else if (xDistance < 0)
                    moveX = _speed;

                if (yDistance > 0)
                    moveY -= _speed;
                else if (yDistance < 0)
                    moveY = _speed;
                SetPosition(moveX, moveY, true);

                // Set the angle
                _rotationInDegrees = _angleOffset + _preRenderedDirections[moveY.ToString() + moveX.ToString()];//(float)Math.Atan2(moveY, moveX);

                // Store the current move into the vector (for angle calculations)
                _currentMoveVector.X = moveX;
                _currentMoveVector.Y = moveY;
            }
            else
            {
                // If a check point has been reached, check if it's the end of the road or go to the next point
                if (_currentWayPoint < _wayPoints.Length - 1)
                    _currentWayPoint++;                    
                else
                    _finished = true;
            }
        }


        /// <summary>
        /// Calculate the distance between two vectors
        /// </summary>
        /// <param name="firstPoint"></param>
        /// <param name="secondPoint"></param>
        /// <returns></returns>
        protected float CalculateDistance(Vector2 firstPoint, Vector2 secondPoint)
        {
            Vector2 relativeDistance = firstPoint - secondPoint;
            return Math.Abs(relativeDistance.X) + Math.Abs(relativeDistance.Y);
        }


        /// <summary>
        /// Check if car can be destroyed
        /// </summary>
        /// <returns></returns>
        public bool IsFinished()
        {
            return _finished;
        }


        /// <summary>
        /// Draw the traffic lights
        /// </summary>
        /// <param name="gameTime"></param>
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // Move the car if needed
            if (_move)
                MoveToWayPoint();

            // Draw the traffic light
            if ( _texturePosition.X >= -50 &&_texturePosition.X <= 818 && _texturePosition.Y >= -50 && _texturePosition.Y <= 818)
                spriteBatch.Draw(_textures[0], _texturePosition, null, Color.White, _rotationInDegrees, _textureCenters[0], 1.0f, SpriteEffects.None, 0);
        }
    }
}
